const fs = require('fs')
const path = require('path')
const MFS = require('memory-fs')
const webpack = require('webpack')
const chokidar = require('chokidar')
const clientConfig = require('./webpack/client')
const serverConfig = require('./webpack/server')

// Read file in real or virtual file systems
const readFile = (fs, file) => {
  try {
    return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8')
  } catch (e) {}
}

module.exports = function setupDevServer ({ server, templatePath, onUpdate }) {
  return new Promise((resolve, reject) => {
    let bundle
    let template
    let clientManifest

    const update = () => {
      if (bundle && clientManifest) {
        resolve()
        onUpdate(bundle, {
          template,
          clientManifest,
        })
      }
    }

    // read template from disk and watch
    template = fs.readFileSync(templatePath, 'utf-8')
    chokidar.watch(templatePath).on('change', () => {
      template = fs.readFileSync(templatePath, 'utf-8')
      console.log('index.html template updated.')
      update()
    })

    // modify client config to work with hot middleware
    clientConfig.entry = ['webpack-hot-middleware/client', clientConfig.entry]
    clientConfig.output.filename = '[name].js'
    clientConfig.plugins.push(
      new webpack.HotModuleReplacementPlugin(),
      new webpack.NoEmitOnErrorsPlugin()
    )

    // dev middleware
    const clientCompiler = webpack(clientConfig)
    const devMiddleware = require('webpack-dev-middleware')(clientCompiler, {
      publicPath: clientConfig.output.publicPath,
      noInfo: true,
    })
    server.use(devMiddleware)
    clientCompiler.plugin('done', stats => {
      stats = stats.toJson()
      stats.errors.forEach(err => console.error(err))
      stats.warnings.forEach(err => console.warn(err))
      if (stats.errors.length) return
      clientManifest = JSON.parse(readFile(
        devMiddleware.fileSystem,
        'vue-ssr-client-manifest.json'
      ))
      update()
    })

    // hot module replacement middleware
    server.use(require('webpack-hot-middleware')(clientCompiler, { heartbeat: 5000 }))

    // watch and update server renderer
    const serverCompiler = webpack(serverConfig)
    const mfs = new MFS()
    serverCompiler.outputFileSystem = mfs
    serverCompiler.watch({}, (err, stats) => {
      if (err) throw err
      stats = stats.toJson()
      if (stats.errors.length) return

      // read bundle generated by vue-ssr-webpack-plugin
      bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))
      update()
    })
  })
}
